#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
ReplayHunter Pro v2.2
企业级会话重放漏洞检测框架 - 增强版
Kali Linux 原生集成版

新增功能:
- 完整的测试过程展示
- 风险等级评估系统
- 漏洞危害分析
- 修复建议生成
- Bug修复和稳定性改进

法律声明: 仅限授权安全测试使用
"""

import sys
import os
import time
import json
import asyncio
import threading
import re
import random
import string
from abc import ABC, abstractmethod
from datetime import datetime
from typing import Dict, List, Optional, Any, Tuple, Set
from difflib import SequenceMatcher
from urllib.parse import urlparse, parse_qs, urlencode, urlunparse
import warnings
import argparse
import hashlib
warnings.filterwarnings('ignore')

# ==================== 核心依赖 ====================
try:
    from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, 
                                 QHBoxLayout, QTabWidget, QLabel, QLineEdit, 
                                 QPushButton, QTextEdit, QProgressBar, QComboBox, 
                                 QFileDialog, QMessageBox, QTableWidget, QTableWidgetItem, 
                                 QHeaderView, QSplitter, QPlainTextEdit, QCheckBox,
                                 QFrame, QGroupBox, QSpinBox, QDoubleSpinBox, QTextBrowser)
    from PyQt5.QtCore import pyqtSignal, QObject, Qt, QThread
    from PyQt5.QtGui import QFont, QTextCharFormat, QColor, QSyntaxHighlighter
    GUI_AVAILABLE = True
except ImportError:
    print("[-] PyQt5 未安装，GUI 模式不可用")
    GUI_AVAILABLE = False

try:
    from rich.console import Console
    from rich.table import Table
    from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TaskProgressColumn
    from rich.panel import Panel
    from rich.syntax import Syntax
    from rich.traceback import install as rich_traceback_install
    rich_traceback_install()
    CLI_AVAILABLE = True
except ImportError:
    print("[-] Rich 库未安装，CLI 体验将降级")
    CLI_AVAILABLE = False

try:
    import aiohttp
    import aiolimiter
    ASYNC_AVAILABLE = True
except ImportError:
    print("[-] aiohttp/aiolimiter 未安装，将使用同步模式")
    ASYNC_AVAILABLE = False

METRICS_AVAILABLE = False
try:
    from prometheus_client import Counter, Histogram, Gauge, start_http_server
    METRICS_AVAILABLE = True
    print("[+] Prometheus 指标功能已启用")
except ImportError:
    print("[-] prometheus_client 未安装，监控功能禁用")

# ==================== 1. 企业级配置管理 ====================
class ConfigManager:
    """支持 YAML/JSON 的层级配置管理器"""
    DEFAULT_CONFIG = {
        "scanner": {
            "timeout": 10,
            "rate_limit": 5,
            "max_threads": 20,
            "retry_count": 3,
            "user_agents": [
                "ReplayHunter/2.2 (Kali Linux; Security Testing)",
                "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"
            ]
        },
        "detection": {
            "similarity_threshold": 0.92,
            "length_tolerance": 50,
            "time_delay_threshold": 3.0,
            "enable_kali_integration": True,
            "static_page_confidence": 0.3,
            "error_page_confidence": 0.2,
            "session_replay_confidence": 0.85
        },
        "security": {
            "require_auth": True,
            "auto_sanitize_logs": True,
            "allowed_targets": []
        },
        "plugins": {
            "kali_tools": {
                "sqlmap": {"enabled": True, "path": "/usr/bin/sqlmap"},
                "nmap": {"enabled": False, "path": "/usr/bin/nmap"},
                "burp": {"enabled": True, "proxy": "127.0.0.1:8080"}
            }
        },
        "metrics": {
            "enabled": True,
            "port": 9090
        }
    }

    def __init__(self, config_path: str = "~/.replayhunter/config.yaml"):
        self.config_path = os.path.expanduser(config_path)
        self.config = self._load_config()

    def _load_config(self) -> Dict[str, Any]:
        """从 YAML/JSON 加载配置"""
        if not os.path.exists(self.config_path):
            os.makedirs(os.path.dirname(self.config_path), exist_ok=True)
            self._save_config(self.DEFAULT_CONFIG)
            return self.DEFAULT_CONFIG

        try:
            with open(self.config_path, 'r') as f:
                if self.config_path.endswith('.yaml') or self.config_path.endswith('.yml'):
                    try:
                        import yaml
                        return yaml.safe_load(f) or {}
                    except ImportError:
                        print(f"[!] YAML 支持库 (PyYAML) 未安装，跳过 {self.config_path}")
                        return self.DEFAULT_CONFIG
                    except Exception as e:
                        print(f"[!] YAML 文件解析失败: {e}")
                        return self.DEFAULT_CONFIG
                else:
                    try:
                        content = f.read().strip()
                        if not content:
                            print(f"[!] 配置文件 {self.config_path} 为空，使用默认配置")
                            return self.DEFAULT_CONFIG
                        return json.loads(content)
                    except json.JSONDecodeError as e:
                        print(f"[!] JSON 文件解析失败: {e}")
                        return self.DEFAULT_CONFIG
        except FileNotFoundError:
            print(f"[!] 配置文件 {self.config_path} 不存在，创建默认配置")
            self._save_config(self.DEFAULT_CONFIG)
            return self.DEFAULT_CONFIG
        except Exception as e:
            print(f"[!] 配置加载失败: {e}，使用默认配置")
            return self.DEFAULT_CONFIG

    def _save_config(self, config: Dict[str, Any]):
        """保存配置到文件"""
        try:
            os.makedirs(os.path.dirname(self.config_path), exist_ok=True)
            with open(self.config_path, 'w') as f:
                if self.config_path.endswith('.yaml') or self.config_path.endswith('.yml'):
                    try:
                        import yaml
                        yaml.dump(config, f, default_flow_style=False, indent=2)
                    except ImportError:
                        print(f"[!] YAML 支持库 (PyYAML) 未安装，无法保存 YAML 格式")
                        json.dump(config, f, indent=2)
                else:
                    json.dump(config, f, indent=2)
        except Exception as e:
            print(f"[!] 配置保存失败: {e}")

    def get(self, key: str, default: Any = None) -> Any:
        """点分式获取配置值，如 scanner.timeout"""
        keys = key.split('.')
        value = self.config
        try:
            for k in keys:
                value = value[k]
            return value
        except (KeyError, TypeError):
            return default

    def set(self, key: str, value: Any):
        """设置配置值并自动保存"""
        keys = key.split('.')
        config = self.config
        for k in keys[:-1]:
            if not isinstance(config, dict):
                config = {}
            config = config.setdefault(k, {})
        if isinstance(config, dict):
            config[keys[-1]] = value
        else:
            print(f"[!] 无法设置配置项 {key}: 路径不是字典结构")
        self._save_config(self.config)

# ==================== 2. 增强的漏洞对象 ====================
class Vulnerability:
    """标准化的漏洞对象 - 增强版"""
    
    # 风险等级定义
    RISK_LEVELS = {
        "CRITICAL": {
            "score": 10.0,
            "color": "#dc3545",
            "description": "严重风险，需立即修复"
        },
        "HIGH": {
            "score": 8.0,
            "color": "#fd7e14",
            "description": "高风险，需尽快修复"
        },
        "MEDIUM": {
            "score": 5.0,
            "color": "#ffc107",
            "description": "中等风险，建议修复"
        },
        "LOW": {
            "score": 3.0,
            "color": "#28a745",
            "description": "低风险，可择期修复"
        },
        "INFO": {
            "score": 0.0,
            "color": "#6c757d",
            "description": "信息性发现"
        }
    }
    
    # 漏洞危害描述
    IMPACT_DESCRIPTIONS = {
        "Session Replay": {
            "impact": "攻击者可以重放用户的会话令牌，冒充合法用户执行操作",
            "consequences": [
                "未经授权访问用户账户",
                "执行敏感操作（如转账、修改密码）",
                "窃取用户数据",
                "破坏数据完整性"
            ],
            "cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N"
        },
        "Time-Based Replay": {
            "impact": "服务器未验证请求时效性，可能导致重放攻击",
            "consequences": [
                "绕过时间限制",
                "重复执行已过期的操作",
                "数据不一致"
            ],
            "cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N"
        },
        "Authentication Bypass": {
            "impact": "认证机制可被绕过，未授权访问受保护资源",
            "consequences": [
                "完全绕过身份验证",
                "访问所有受保护功能",
                "系统完整性受损"
            ],
            "cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H"
        },
        "Session Token Replay": {
            "impact": "会话令牌可被重复使用，未实现一次性令牌机制",
            "consequences": [
                "会话劫持",
                "持久化未授权访问",
                "用户隐私泄露"
            ],
            "cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N"
        },
        "Parameter Pollution": {
            "impact": "参数污染可能导致业务逻辑错误",
            "consequences": [
                "绕过输入验证",
                "数据污染",
                "业务逻辑错误"
            ],
            "cvss_vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N"
        },
        "Redirect Replay": {
            "impact": "重定向行为可被重放，可能导致钓鱼攻击",
            "consequences": [
                "开放重定向",
                "钓鱼攻击",
                "凭证窃取"
            ],
            "cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"
        }
    }
    
    # 修复建议
    REMEDIATION_ADVICE = {
        "Session Replay": {
            "immediate": [
                "实现一次性令牌（Nonce）机制",
                "为每个请求生成唯一标识符",
                "验证请求的时间戳，拒绝过期请求"
            ],
            "short_term": [
                "实现请求签名验证",
                "使用HMAC或数字签名保护请求完整性",
                "记录所有已使用的令牌，防止重放"
            ],
            "long_term": [
                "采用OAuth 2.0或OpenID Connect",
                "实现完整的会话管理机制",
                "定期进行安全审计"
            ],
            "code_example": """
# Python 示例：实现 Nonce 机制
import secrets
import time
from functools import wraps

class NonceManager:
    def __init__(self, ttl=300):  # 5分钟有效期
        self.used_nonces = {}
        self.ttl = ttl
    
    def generate_nonce(self):
        nonce = secrets.token_urlsafe(32)
        timestamp = time.time()
        self.used_nonces[nonce] = timestamp
        return nonce
    
    def verify_nonce(self, nonce):
        if nonce not in self.used_nonces:
            return False
        timestamp = self.used_nonces[nonce]
        if time.time() - timestamp > self.ttl:
            del self.used_nonces[nonce]
            return False
        del self.used_nonces[nonce]  # 一次性使用
        return True

def require_nonce(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        nonce = request.headers.get('X-Nonce')
        if not nonce or not nonce_manager.verify_nonce(nonce):
            return jsonify({'error': 'Invalid or expired nonce'}), 403
        return f(*args, **kwargs)
    return decorated_function
            """
        },
        "Authentication Bypass": {
            "immediate": [
                "立即启用严格的身份验证",
                "验证所有请求的授权令牌",
                "实现多因素认证（MFA）"
            ],
            "short_term": [
                "审查所有认证端点",
                "实现基于角色的访问控制（RBAC）",
                "记录所有认证失败尝试"
            ],
            "long_term": [
                "采用零信任安全模型",
                "实现持续身份验证",
                "集成企业级身份管理系统"
            ],
            "code_example": """
# Python Flask 示例：JWT 认证
from flask import Flask, request, jsonify
from functools import wraps
import jwt
import datetime

SECRET_KEY = 'your-secret-key'

def token_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get('Authorization')
        if not token:
            return jsonify({'error': 'Token is missing'}), 401
        
        try:
            token = token.split()[1]  # Bearer <token>
            data = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
            # 验证时间戳
            if datetime.datetime.fromtimestamp(data['exp']) < datetime.datetime.utcnow():
                return jsonify({'error': 'Token expired'}), 401
        except Exception as e:
            return jsonify({'error': 'Invalid token'}), 401
        
        return f(*args, **kwargs)
    return decorated
            """
        },
        "Time-Based Replay": {
            "immediate": [
                "为所有请求添加时间戳验证",
                "拒绝超过时间窗口的请求（如5分钟）",
                "实现服务器端时间同步"
            ],
            "short_term": [
                "实现请求序列号机制",
                "记录请求历史，检测异常模式",
                "使用分布式缓存存储已处理请求"
            ],
            "long_term": [
                "采用基于时间的一次性密码（TOTP）",
                "实现完整的防重放框架",
                "集成安全信息和事件管理（SIEM）系统"
            ],
            "code_example": """
# Python 示例：时间戳验证
import time
import hashlib
import hmac

def verify_timestamp(timestamp, signature, secret_key, tolerance=300):
    '''
    验证请求时间戳
    tolerance: 时间容差（秒）
    '''
    current_time = time.time()
    
    # 检查时间戳是否在容差范围内
    if abs(current_time - timestamp) > tolerance:
        return False, "Request expired"
    
    # 验证签名
    expected_signature = hmac.new(
        secret_key.encode(),
        str(timestamp).encode(),
        hashlib.sha256
    ).hexdigest()
    
    if not hmac.compare_digest(signature, expected_signature):
        return False, "Invalid signature"
    
    return True, "Valid"

# 使用示例
@app.route('/api/action', methods=['POST'])
def protected_action():
    timestamp = float(request.headers.get('X-Timestamp', 0))
    signature = request.headers.get('X-Signature', '')
    
    valid, message = verify_timestamp(timestamp, signature, SECRET_KEY)
    if not valid:
        return jsonify({'error': message}), 403
    
    # 处理业务逻辑
    return jsonify({'status': 'success'})
            """
        }
    }
    
    def __init__(self, vuln_type: str, param: str, payload: str, 
                 severity: str, evidence: str, confidence: float = 0.8,
                 test_details: Optional[Dict[str, Any]] = None):
        self.type = vuln_type
        self.param = param
        self.payload = payload
        self.severity = severity
        self.evidence = evidence
        self.confidence = confidence
        self.timestamp = datetime.now()
        self.test_details = test_details or {}
        
        # 计算风险评分
        self.risk_score = self._calculate_risk_score()
        
        # 获取危害描述
        self.impact = self._get_impact_description()
        
        # 获取修复建议
        self.remediation = self._get_remediation_advice()

    def _calculate_risk_score(self) -> float:
        """计算综合风险评分 (0-10)"""
        base_score = self.RISK_LEVELS.get(self.severity, {}).get("score", 0.0)
        # 根据置信度调整评分
        adjusted_score = base_score * self.confidence
        return round(adjusted_score, 2)

    def _get_impact_description(self) -> Dict[str, Any]:
        """获取漏洞危害描述"""
        return self.IMPACT_DESCRIPTIONS.get(self.type, {
            "impact": "未知影响",
            "consequences": ["需要进一步分析"],
            "cvss_vector": "N/A"
        })

    def _get_remediation_advice(self) -> Dict[str, Any]:
        """获取修复建议"""
        return self.REMEDIATION_ADVICE.get(self.type, {
            "immediate": ["请咨询安全专家"],
            "short_term": ["进行安全评估"],
            "long_term": ["建立安全开发流程"],
            "code_example": "# 请参考 OWASP 指南"
        })

    def to_dict(self) -> Dict[str, Any]:
        return {
            "type": self.type,
            "param": self.param,
            "payload": self.payload,
            "severity": self.severity,
            "evidence": self.evidence,
            "confidence": self.confidence,
            "risk_score": self.risk_score,
            "timestamp": self.timestamp.isoformat(),
            "impact": self.impact,
            "remediation": self.remediation,
            "test_details": self.test_details
        }

    def __hash__(self):
        """用于去重"""
        return hash((self.type, self.param, self.payload))

    def __eq__(self, other):
        """用于去重"""
        if not isinstance(other, Vulnerability):
            return False
        return (self.type, self.param, self.payload) == (other.type, other.param, other.payload)

# ==================== 3. 测试过程记录器 ====================
class TestProcess:
    """测试过程详细记录"""
    
    def __init__(self):
        self.steps = []
        self.start_time = None
        self.end_time = None
        self.requests_sent = 0
        self.responses_received = 0
        self.errors_encountered = 0
    
    def start(self):
        """开始测试"""
        self.start_time = datetime.now()
        self.add_step("测试开始", "初始化扫描引擎", "INFO")
    
    def add_step(self, title: str, description: str, level: str = "INFO", 
                 details: Optional[Dict[str, Any]] = None):
        """添加测试步骤"""
        step = {
            "timestamp": datetime.now().isoformat(),
            "title": title,
            "description": description,
            "level": level,
            "details": details or {}
        }
        self.steps.append(step)
    
    def end(self):
        """结束测试"""
        self.end_time = datetime.now()
        duration = (self.end_time - self.start_time).total_seconds()
        self.add_step(
            "测试完成", 
            f"总耗时: {duration:.2f}秒, 发送请求: {self.requests_sent}, 接收响应: {self.responses_received}",
            "SUCCESS"
        )
    
    def to_html(self) -> str:
        """生成 HTML 格式的测试过程"""
        html = """
        <div class="test-process">
            <h3>📋 测试过程详情</h3>
            <div class="timeline">
        """
        
        for step in self.steps:
            level_colors = {
                "INFO": "#17a2b8",
                "SUCCESS": "#28a745",
                "WARNING": "#ffc107",
                "ERROR": "#dc3545"
            }
            color = level_colors.get(step['level'], "#6c757d")
            
            html += f"""
            <div class="timeline-item" style="border-left: 3px solid {color};">
                <div class="timeline-time">{step['timestamp']}</div>
                <div class="timeline-title">{step['title']}</div>
                <div class="timeline-description">{step['description']}</div>
            """
            
            if step['details']:
                html += "<div class='timeline-details'>"
                for key, value in step['details'].items():
                    html += f"<div><strong>{key}:</strong> {value}</div>"
                html += "</div>"
            
            html += "</div>"
        
        html += """
            </div>
        </div>
        """
        return html

# ==================== 4. 检测策略（保持原有代码，添加测试过程记录）====================
class DetectionStrategy(ABC):
    """策略模式基类"""
    @abstractmethod
    def analyze(self, baseline: Dict[str, Any], test: Dict[str, Any], 
                context: Dict[str, Any]) -> Optional[Vulnerability]:
        """分析请求对，返回漏洞对象或 None"""
        pass

class TimeBasedStrategy(DetectionStrategy):
    """时间盲注检测策略"""
    def __init__(self, threshold: float = 3.0):
        self.threshold = threshold

    def analyze(self, baseline: Dict[str, Any], test: Dict[str, Any], 
                context: Dict[str, Any]) -> Optional[Vulnerability]:
        try:
            test_process = context.get('test_process')
            if test['duration'] >= self.threshold:
                if abs(test['duration'] - baseline['duration']) > self.threshold * 0.8:
                    if test_process:
                        test_process.add_step(
                            "时间延迟检测",
                            f"发现异常延迟: {test['duration']:.2f}s",
                            "WARNING",
                            {
                                "基准耗时": f"{baseline['duration']:.2f}s",
                                "测试耗时": f"{test['duration']:.2f}s",
                                "差值": f"{abs(test['duration'] - baseline['duration']):.2f}s"
                            }
                        )
                    
                    return Vulnerability(
                        vuln_type="Time-Based Replay",
                        param=context.get('param', 'unknown'),
                        payload=context.get('payload', 'unknown'),
                        severity="HIGH",
                        evidence=f"响应延迟 {test['duration']:.2f}s (基准: {baseline['duration']:.2f}s)",
                        confidence=0.9,
                        test_details={
                            "baseline_duration": baseline['duration'],
                            "test_duration": test['duration'],
                            "threshold": self.threshold
                        }
                    )
        except KeyError as e:
            print(f"[!] TimeBasedStrategy 缺少键: {e}")
        return None

class SimilarityStrategy(DetectionStrategy):
    """智能相似度比对策略"""
    def __init__(self, threshold: float = 0.92, length_tolerance: int = 50):
        self.threshold = threshold
        self.length_tolerance = length_tolerance
        self.dynamic_patterns = [
            r'\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}',
            r'\d{13}',
            r'[a-f0-9]{32}',
            r'[a-f0-9]{64}',
            r'[a-f0-9]{128}',
            r'csrf_token=\w+',
            r'sessionid=\w+',
            r'jwt=eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*',
            r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}',
            r'timestamp=\d+',
            r'nonce=\w+',
            r'state=\w+',
        ]
        self.static_indicators = [
            r'<title>.*[Ll]ogin.*</title>',
            r'<title>.*[Ee]rror.*</title>',
            r'<title>.*[404|403|500].*</title>',
            r'error.*[Pp]age',
            r'[Ll]ogin.*[Ff]orm',
            r'[Aa]ccess.*[Dd]enied',
            r'[Pp]ermission.*[Dd]enied',
            r'[Uu]nauthorized',
        ]
        self.error_indicators = [
            r'[Ee]rror',
            r'[Ee]xception',
            r'[Ff]ail',
            r'[Ii]nvalid',
            r'[Uu]nauthorized',
            r'[Aa]ccess.*[Dd]enied',
            r'[Pp]ermission.*[Dd]enied',
            r'[Nn]ot.*[Ff]ound',
            r'[Ss]erver.*[Ee]rror',
        ]

    def _normalize(self, text: str) -> str:
        """移除动态内容"""
        for pattern in self.dynamic_patterns:
            text = re.sub(pattern, '[DYNAMIC]', text)
        return text

    def _is_static_page(self, text: str) -> bool:
        """检测是否为静态页面"""
        for indicator in self.static_indicators:
            if re.search(indicator, text, re.IGNORECASE):
                return True
        return False

    def _contains_error(self, text: str) -> bool:
        """检测响应是否包含错误信息"""
        for pattern in self.error_indicators:
            if re.search(pattern, text, re.IGNORECASE):
                return True
        return False

    def analyze(self, baseline: Dict[str, Any], test: Dict[str, Any], 
                context: Dict[str, Any]) -> Optional[Vulnerability]:
        try:
            test_process = context.get('test_process')
            
            # 状态码检查
            if baseline['status_code'] != test['status_code']:
                if test_process:
                    test_process.add_step(
                        "状态码检查",
                        "状态码不同，跳过相似度分析",
                        "INFO",
                        {
                            "基准状态码": baseline['status_code'],
                            "测试状态码": test['status_code']
                        }
                    )
                return None
            
            # 长度检查
            length_diff = abs(baseline['length'] - test['length'])
            if length_diff > self.length_tolerance:
                if test_process:
                    test_process.add_step(
                        "长度检查",
                        "响应长度差异过大，跳过相似度分析",
                        "INFO",
                        {
                            "基准长度": baseline['length'],
                            "测试长度": test['length'],
                            "差值": length_diff,
                            "容差": self.length_tolerance
                        }
                    )
                return None
            
            # 内容相似度检查
            base_norm = self._normalize(baseline['text'])
            test_norm = self._normalize(test['text'])
            similarity = SequenceMatcher(None, base_norm, test_norm).ratio()
            
            if test_process:
                test_process.add_step(
                    "相似度分析",
                    f"计算相似度: {similarity:.2%}",
                    "INFO",
                    {
                        "相似度": f"{similarity:.2%}",
                        "阈值": f"{self.threshold:.2%}",
                        "基准长度": baseline['length'],
                        "测试长度": test['length']
                    }
                )
            
            if similarity >= self.threshold:
                # 检查是否为错误页面
                if self._contains_error(baseline['text']):
                    if test_process:
                        test_process.add_step(
                            "页面类型识别",
                            "识别为错误页面",
                            "INFO"
                        )
                    return Vulnerability(
                        vuln_type="Error Page Replay",
                        param=context.get('param', 'unknown'),
                        payload=context.get('payload', 'unknown'),
                        severity="INFO",
                        evidence=f"错误页面重放，相似度 {similarity:.2%}",
                        confidence=0.2,
                        test_details={
                            "similarity": similarity,
                            "page_type": "error_page"
                        }
                    )
                
                # 检查是否为静态页面
                is_static = self._is_static_page(baseline['text'])
                if is_static:
                    if test_process:
                        test_process.add_step(
                            "页面类型识别",
                            "识别为静态页面（登录/错误页）",
                            "INFO"
                        )
                    return Vulnerability(
                        vuln_type="Static Page Replay",
                        param=context.get('param', 'unknown'),
                        payload=context.get('payload', 'unknown'),
                        severity="INFO",
                        evidence=f"静态页面（登录/错误页），相似度 {similarity:.2%}",
                        confidence=0.3,
                        test_details={
                            "similarity": similarity,
                            "page_type": "static_page"
                        }
                    )
                
                # 检查是否为完全相同的静态内容
                if similarity == 1.0 and baseline['length'] > 1000:
                    if test_process:
                        test_process.add_step(
                            "内容分析",
                            "检测到完全相同的大型静态内容",
                            "WARNING"
                        )
                    return Vulnerability(
                        vuln_type="Session Replay",
                        param=context.get('param', 'unknown'),
                        payload=context.get('payload', 'unknown'),
                        severity="LOW",
                        evidence=f"完全相同的静态内容，相似度 {similarity:.2%}，可能是静态资源",
                        confidence=0.4,
                        test_details={
                            "similarity": similarity,
                            "content_length": baseline['length'],
                            "page_type": "large_static_content"
                        }
                    )
                else:
                    # 正常的会话重放检测
                    if test_process:
                        test_process.add_step(
                            "漏洞确认",
                            "检测到会话重放漏洞",
                            "WARNING",
                            {
                                "相似度": f"{similarity:.2%}",
                                "长度差": length_diff
                            }
                        )
                    return Vulnerability(
                        vuln_type="Session Replay",
                        param=context.get('param', 'unknown'),
                        payload=context.get('payload', 'unknown'),
                        severity="MEDIUM",
                        evidence=f"相似度 {similarity:.2%}，长度差 {length_diff}",
                        confidence=0.85,
                        test_details={
                            "similarity": similarity,
                            "length_difference": length_diff,
                            "baseline_status": baseline['status_code'],
                            "test_status": test['status_code']
                        }
                    )
            return None
        except KeyError as e:
            print(f"[!] SimilarityStrategy 缺少键: {e}")
            return None
        except Exception as e:
            print(f"[!] SimilarityStrategy 分析出错: {e}")
            return None

class ParameterPollutionStrategy(DetectionStrategy):
    """参数污染检测策略"""
    def analyze(self, baseline: Dict[str, Any], test: Dict[str, Any], 
                context: Dict[str, Any]) -> Optional[Vulnerability]:
        try:
            test_process = context.get('test_process')
            if context.get('polluted'):
                if baseline['status_code'] != test['status_code'] or \
                   abs(baseline['length'] - test['length']) > 100:
                    if test_process:
                        test_process.add_step(
                            "参数污染检测",
                            "参数污染影响了服务器响应",
                            "WARNING"
                        )
                    return Vulnerability(
                        vuln_type="Parameter Pollution",
                        param=context.get('param', 'unknown'),
                        payload=context.get('payload', 'unknown'),
                        severity="LOW",
                        evidence="参数污染影响了服务器响应",
                        confidence=0.6,
                        test_details={
                            "baseline_status": baseline['status_code'],
                            "test_status": test['status_code'],
                            "length_difference": abs(baseline['length'] - test['length'])
                        }
                    )
        except KeyError as e:
            print(f"[!] ParameterPollutionStrategy 缺少键: {e}")
        except Exception as e:
            print(f"[!] ParameterPollutionStrategy 分析出错: {e}")
        return None

class ResponseBehaviorStrategy(DetectionStrategy):
    """响应行为分析策略"""
    def analyze(self, baseline: Dict[str, Any], test: Dict[str, Any], 
                context: Dict[str, Any]) -> Optional[Vulnerability]:
        try:
            test_process = context.get('test_process')
            auth_error_codes = {401, 403}
            
            if baseline['status_code'] in auth_error_codes:
                if test_process:
                    test_process.add_step(
                        "认证检查",
                        "基准请求返回认证错误，跳过分析",
                        "INFO"
                    )
                return None
            
            if test['status_code'] in auth_error_codes:
                if test_process:
                    test_process.add_step(
                        "认证绕过检测",
                        "重放后返回认证错误，可能存在认证绕过",
                        "ERROR"
                    )
                return Vulnerability(
                    vuln_type="Authentication Bypass",
                    param=context.get('param', 'unknown'),
                    payload=context.get('payload', 'unknown'),
                    severity="HIGH",
                    evidence=f"重放后返回 {test['status_code']}，可能绕过认证",
                    confidence=0.9,
                    test_details={
                        "baseline_status": baseline['status_code'],
                        "test_status": test['status_code']
                    }
                )
            
            baseline_cookies = baseline.get('headers', {}).get('Set-Cookie', '')
            test_cookies = test.get('headers', {}).get('Set-Cookie', '')
            if baseline_cookies and baseline_cookies == test_cookies:
                if test_process:
                    test_process.add_step(
                        "Cookie 分析",
                        "检测到相同的会话Cookie",
                        "WARNING"
                    )
                return Vulnerability(
                    vuln_type="Session Token Replay",
                    param="Set-Cookie",
                    payload="replay",
                    severity="HIGH",
                    evidence="重放后返回相同的会话Cookie，会话令牌未失效",
                    confidence=0.85,
                    test_details={
                        "cookie_value": baseline_cookies[:50] + "..."
                    }
                )
            
            if baseline['status_code'] in {301, 302} and test['status_code'] == baseline['status_code']:
                base_location = baseline.get('headers', {}).get('Location', '')
                test_location = test.get('headers', {}).get('Location', '')
                if base_location and base_location == test_location:
                    if test_process:
                        test_process.add_step(
                            "重定向分析",
                            "检测到相同的重定向目标",
                            "WARNING"
                        )
                    return Vulnerability(
                        vuln_type="Redirect Replay",
                        param="Location",
                        payload="replay",
                        severity="MEDIUM",
                        evidence="重放后重定向到相同位置，可能缺乏重放保护",
                        confidence=0.7,
                        test_details={
                            "redirect_location": base_location
                        }
                    )
            
            return None
        except Exception as e:
            print(f"[!] ResponseBehaviorStrategy 分析出错: {e}")
            return None

# ==================== 5. 异步扫描引擎（增强版）====================
class AsyncReplayScanner:
    """高性能异步扫描引擎 - 增强版"""
    def __init__(self, config: ConfigManager):
        self.config = config
        self.session = None
        self.limiter = aiolimiter.AsyncLimiter(
            config.get("scanner.rate_limit", 5), 
            1
        ) if ASYNC_AVAILABLE else None
        self.strategies = self._load_strategies()
        self.vulnerabilities = []
        self.metrics_available_instance = METRICS_AVAILABLE
        self.test_process = TestProcess()
        
        # 指标
        self.req_counter = None
        self.vuln_counter = None
        self.duration_hist = None
        
        if self.metrics_available_instance:
            try:
                self.req_counter = Counter('replayhunter_requests_total', 'Total requests', ['status'])
                self.vuln_counter = Counter('replayhunter_vulnerabilities_total', 'Vulnerabilities', ['type'])
                self.duration_hist = Histogram('replayhunter_request_duration_seconds', 'Request duration')
            except Exception as e:
                print(f"[!] 初始化 Prometheus 指标失败: {e}")
                self.metrics_available_instance = False

    def _load_strategies(self) -> List[DetectionStrategy]:
        """动态加载检测策略"""
        strategies = []
        strategies.append(TimeBasedStrategy(
            threshold=self.config.get("detection.time_delay_threshold", 3.0)
        ))
        strategies.append(SimilarityStrategy(
            threshold=self.config.get("detection.similarity_threshold", 0.92),
            length_tolerance=self.config.get("detection.length_tolerance", 50)
        ))
        strategies.append(ParameterPollutionStrategy())
        strategies.append(ResponseBehaviorStrategy())
        return strategies

    async def _send_request(self, session: aiohttp.ClientSession, 
                           url: str, method: str, headers: dict, 
                           data: dict, request_id: str) -> Dict[str, Any]:
        """发送异步请求"""
        try:
            self.test_process.requests_sent += 1
            
            if self.limiter:
                async with self.limiter:
                    start_time = time.time()
                    if method == "GET":
                        async with session.get(url, headers=headers, params=data, 
                                              timeout=aiohttp.ClientTimeout(total=self.config.get("scanner.timeout", 10))) as resp:
                            text = await resp.text()
                            resp_headers = dict(resp.headers)
                    else:
                        async with session.post(url, headers=headers, data=json.dumps(data) if isinstance(data, dict) else data, 
                                               timeout=aiohttp.ClientTimeout(total=self.config.get("scanner.timeout", 10))) as resp:
                            text = await resp.text()
                            resp_headers = dict(resp.headers)
            else:
                start_time = time.time()
                if method == "GET":
                    async with session.get(url, headers=headers, params=data, 
                                          timeout=aiohttp.ClientTimeout(total=self.config.get("scanner.timeout", 10))) as resp:
                        text = await resp.text()
                        resp_headers = dict(resp.headers)
                else:
                    async with session.post(url, headers=headers, data=json.dumps(data) if isinstance(data, dict) else data, 
                                           timeout=aiohttp.ClientTimeout(total=self.config.get("scanner.timeout", 10))) as resp:
                        text = await resp.text()
                        resp_headers = dict(resp.headers)
            
            end_time = time.time()
            duration = round(end_time - start_time, 3)
            
            self.test_process.responses_received += 1
            self.test_process.add_step(
                f"请求 {request_id}",
                f"状态码: {resp.status}, 耗时: {duration}s, 长度: {len(text)}",
                "INFO",
                {
                    "方法": method,
                    "URL": url,
                    "状态码": resp.status,
                    "响应长度": len(text),
                    "耗时": f"{duration}s"
                }
            )
            
            result = {
                "id": request_id,
                "status_code": resp.status,
                "length": len(text),
                "text": text,
                "duration": duration,
                "headers": resp_headers
            }
            
            if self.metrics_available_instance:
                try:
                    if self.req_counter:
                        self.req_counter.labels(status=resp.status).inc()
                    if self.duration_hist:
                        self.duration_hist.observe(duration)
                except Exception as e:
                    print(f"[!] 记录 Prometheus 指标失败: {e}")
            
            return result
        except asyncio.TimeoutError:
            self.test_process.errors_encountered += 1
            self.test_process.add_step(
                f"请求超时 {request_id}",
                f"请求超时: {url}",
                "ERROR"
            )
            return {"error": "Request timeout"}
        except Exception as e:
            self.test_process.errors_encountered += 1
            self.test_process.add_step(
                f"请求失败 {request_id}",
                f"错误: {str(e)}",
                "ERROR"
            )
            return {"error": str(e)}

    async def scan(self, target: Dict[str, Any], 
                  callback: Optional[callable] = None) -> List[Vulnerability]:
        """执行异步扫描"""
        self.test_process.start()
        
        url = target['url']
        method = target['method']
        headers = target['headers']
        data = target['data']

        try:
            connector = aiohttp.TCPConnector(
                limit=self.config.get("scanner.max_threads", 20),
                ssl=False
            )
            async with aiohttp.ClientSession(connector=connector) as session:
                # 1. 基准请求
                if callback: callback("发送基准请求...")
                self.test_process.add_step(
                    "基准请求",
                    f"发送基准请求到 {url}",
                    "INFO"
                )
                baseline = await self._send_request(session, url, method, headers, data, "Baseline")
                if 'error' in baseline:
                    if callback: callback(f"基准请求失败: {baseline['error']}")
                    self.test_process.add_step(
                        "基准请求失败",
                        baseline['error'],
                        "ERROR"
                    )
                    self.test_process.end()
                    return []

                # 2. 重放请求（多次）
                if callback: callback("执行重放测试...")
                self.test_process.add_step(
                    "重放测试",
                    "开始执行3次重放请求",
                    "INFO"
                )
                replay_tasks = []
                for i in range(3):
                    task = self._send_request(session, url, method, headers, data, f"Replay-{i+1}")
                    replay_tasks.append(task)
                replays = await asyncio.gather(*replay_tasks)

                # 3. 参数污染测试
                if callback: callback("执行参数污染测试...")
                self.test_process.add_step(
                    "参数污染测试",
                    "添加污染参数进行测试",
                    "INFO"
                )
                polluted_data = data.copy() if isinstance(data, dict) else {}
                polluted_data['replayhunter_pollution'] = ''.join(random.choices(string.ascii_letters, k=20))
                pollution = await self._send_request(session, url, method, headers, polluted_data, "Pollution")

                # 4. 策略分析
                self.test_process.add_step(
                    "漏洞分析",
                    "开始执行检测策略分析",
                    "INFO"
                )
                all_requests = [baseline] + replays + [pollution]
                
                potential_vulns = []
                for strategy in self.strategies:
                    strategy_name = strategy.__class__.__name__
                    self.test_process.add_step(
                        f"执行策略: {strategy_name}",
                        f"运行 {strategy_name} 检测",
                        "INFO"
                    )
                    
                    for req in all_requests[1:]:
                        if 'error' not in req:
                            context = {
                                "param": "session",
                                "payload": "replay",
                                "all_requests": all_requests,
                                "test_process": self.test_process
                            }
                            vuln = strategy.analyze(baseline, req, context)
                            if vuln:
                                potential_vulns.append(vuln)
                
                # 去重和聚合
                self.vulnerabilities = self._deduplicate_and_aggregate(potential_vulns)
                
                # 记录漏洞
                for vuln in self.vulnerabilities:
                    if self.metrics_available_instance:
                        try:
                            if self.vuln_counter:
                                self.vuln_counter.labels(type=vuln.type).inc()
                        except Exception as e:
                            print(f"[!] 记录 Prometheus 漏洞指标失败: {e}")
                    
                    self.test_process.add_step(
                        f"发现漏洞: {vuln.type}",
                        vuln.evidence,
                        "WARNING",
                        {
                            "风险等级": vuln.severity,
                            "置信度": f"{vuln.confidence:.0%}",
                            "风险评分": vuln.risk_score
                        }
                    )
                    
                    if callback: callback(f"发现漏洞: {vuln.type} (置信度: {vuln.confidence:.0%})")

                self.test_process.end()
                if callback: callback(f"扫描完成，发现 {len(self.vulnerabilities)} 个漏洞")
                return self.vulnerabilities
        except Exception as e:
            print(f"[!] 扫描过程中发生异常: {e}")
            self.test_process.add_step(
                "扫描异常",
                str(e),
                "ERROR"
            )
            self.test_process.end()
            return []
    
    def _deduplicate_and_aggregate(self, vulns: List[Vulnerability]) -> List[Vulnerability]:
        """漏洞去重和聚合"""
        vuln_dict: Dict[Tuple[str, str], Vulnerability] = {}
        for vuln in vulns:
            key = (vuln.type, vuln.param)
            if key not in vuln_dict or vuln.confidence > vuln_dict[key].confidence:
                vuln_dict[key] = vuln
        
        result = list(vuln_dict.values())
        result.sort(key=lambda v: v.risk_score, reverse=True)
        return result

# ==================== 6. Kali Linux 工具集成（保持不变）====================
class KaliToolIntegration:
    """Kali Linux 原生工具集成"""
    def __init__(self, config: ConfigManager):
        self.config = config
        self.tools = {
            "sqlmap": {
                "path": config.get("plugins.kali_tools.sqlmap.path", "/usr/bin/sqlmap"),
                "enabled": config.get("plugins.kali_tools.sqlmap.enabled", True),
                "category": "database"
            },
            "nmap": {
                "path": config.get("plugins.kali_tools.nmap.path", "/usr/bin/nmap"),
                "enabled": config.get("plugins.kali_tools.nmap.enabled", False),
                "category": "network"
            },
            "nikto": {
                "path": config.get("plugins.kali_tools.nikto.path", "/usr/bin/nikto"),
                "enabled": config.get("plugins.kali_tools.nikto.enabled", False),
                "category": "web"
            },
            "burp": {
                "proxy": config.get("plugins.kali_tools.burp.proxy", "127.0.0.1:8080"),
                "enabled": config.get("plugins.kali_tools.burp.enabled", True),
                "category": "proxy"
            }
        }

    def is_tool_available(self, tool_name: str) -> bool:
        """检查工具是否可用"""
        tool = self.tools.get(tool_name)
        if not tool or not tool['enabled']:
            return False
        if tool['category'] == 'proxy':
            import socket
            try:
                host, port = tool['proxy'].split(':')
                with socket.create_connection((host, int(port)), timeout=2):
                    pass
                return True
            except (ValueError, socket.gaierror, socket.timeout, OSError):
                return False
        else:
            return os.path.isfile(tool.get('path', '')) and os.access(tool.get('path', ''), os.X_OK)

    def run_sqlmap_check(self, target_url: str, vulnerable_param: str) -> Dict[str, Any]:
        """运行 sqlmap 深度检测"""
        if not self.is_tool_available("sqlmap"):
            return {"error": "sqlmap 未安装或未启用"}
        import subprocess
        cmd = [
            self.tools['sqlmap']['path'],
            "-u", target_url,
            "-p", vulnerable_param,
            "--batch",
            "--random-agent",
            "--level", "1",
            "--risk", "1",
            "-f",
            "--output-dir", "/tmp/replayhunter_sqlmap"
        ]
        try:
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
            return {
                "exit_code": result.returncode,
                "stdout": result.stdout,
                "stderr": result.stderr,
                "vulnerable": "is vulnerable" in result.stdout.lower()
            }
        except subprocess.TimeoutExpired:
            return {"error": "sqlmap 执行超时"}
        except Exception as e:
            return {"error": str(e)}

    def import_from_burp(self, xml_file: str) -> List[Dict[str, Any]]:
        """从 Burp Suite XML 导入请求"""
        try:
            import xml.etree.ElementTree as ET
            tree = ET.parse(xml_file)
            root = tree.getroot()
            requests = []
            for item in root.findall('.//item'):
                url_elem = item.find('url')
                method_elem = item.find('method')
                if url_elem is None or method_elem is None:
                    print(f"[!] 跳过无效的 Burp 项: 缺少 URL 或 Method")
                    continue
                url = url_elem.text
                method = method_elem.text

                headers = {}
                headers_elem = item.find('headers')
                if headers_elem is not None:
                    for header in headers_elem.findall('header'):
                        name = header.get('name')
                        value = header.text
                        if name and value:
                            headers[name] = value

                data_elem = item.find('request')
                data = data_elem.text if data_elem is not None else ""

                requests.append({
                    "url": url,
                    "method": method,
                    "headers": headers,
                    "data": data
                })
            return requests
        except Exception as e:
            return [{"error": f"Burp 文件解析失败: {e}"}]

# ==================== 7. 企业级 GUI (Dark Mode) - 增强版 ====================
class DarkTheme:
    """Kali Linux 风格 Dark Mode 主题"""
    STYLESHEET = """
    QMainWindow {
        background-color: #1e1e1e;
        color: #d4d4d4;
    }
    QWidget {
        background-color: #1e1e1e;
        color: #d4d4d4;
    }
    QTabWidget::pane {
        border: 1px solid #3e3e42;
        background: #252526;
    }
    QTabBar::tab {
        background: #2d2d30;
        color: #cccccc;
        padding: 8px 20px;
        margin-right: 2px;
    }
    QTabBar::tab:selected {
        background: #007acc;
        color: white;
    }
    QPushButton {
        background-color: #007acc;
        color: white;
        padding: 8px 16px;
        border: none;
        border-radius: 4px;
        font-weight: bold;
    }
    QPushButton:hover {
        background-color: #005a9e;
    }
    QPushButton:disabled {
        background-color: #3e3e42;
        color: #6e6e70;
    }
    QLineEdit, QPlainTextEdit, QTextEdit {
        background-color: #2d2d30;
        color: #d4d4d4;
        border: 1px solid #3e3e42;
        padding: 6px;
        border-radius: 3px;
        selection-background-color: #264f78;
    }
    QTableWidget {
        background-color: #1e1e1e;
        color: #d4d4d4;
        gridline-color: #3e3e42;
        border: 1px solid #3e3e42;
    }
    QHeaderView::section {
        background-color: #2d2d30;
        color: #d4d4d4;
        padding: 6px;
        border: none;
        font-weight: bold;
    }
    QProgressBar {
        background-color: #3e3e42;
        border: none;
        height: 12px;
        border-radius: 6px;
    }
    QProgressBar::chunk {
        background-color: #007acc;
        border-radius: 6px;
    }
    QCheckBox {
        color: #d4d4d4;
        spacing: 8px;
    }
    QCheckBox::indicator {
        width: 18px;
        height: 18px;
        border: 1px solid #3e3e42;
        border-radius: 3px;
    }
    QCheckBox::indicator:checked {
        background-color: #007acc;
    }
    QLabel {
        color: #d4d4d4;
    }
    QGroupBox {
        border: 1px solid #3e3e42;
        border-radius: 4px;
        margin-top: 8px;
        padding-top: 8px;
    }
    QGroupBox::title {
        subcontrol-origin: margin;
        left: 10px;
        padding: 0 5px;
        color: #007acc;
        font-weight: bold;
    }
    QTextBrowser {
        background-color: #2d2d30;
        color: #d4d4d4;
        border: 1px solid #3e3e42;
    }
    """

class DiffHighlighter(QSyntaxHighlighter):
    """差异高亮显示"""
    def __init__(self, parent):
        super().__init__(parent)
        self.format_add = QTextCharFormat()
        self.format_add.setBackground(QColor("#2d5016"))
        self.format_add.setForeground(QColor("#4ade80"))
        self.format_remove = QTextCharFormat()
        self.format_remove.setBackground(QColor("#5a1a1a"))
        self.format_remove.setForeground(QColor("#f87171"))
        self.format_change = QTextCharFormat()
        self.format_change.setBackground(QColor("#3b2c1e"))
        self.format_change.setForeground(QColor("#fbbf24"))

    def highlightBlock(self, text: str):
        if text.startswith('+'):
            self.setFormat(0, len(text), self.format_add)
        elif text.startswith('-'):
            self.setFormat(0, len(text), self.format_remove)
        elif text.startswith('@@'):
            self.setFormat(0, len(text), self.format_change)

class MainWindow(QMainWindow):
    """主窗口 - 增强版"""
    scan_started = pyqtSignal()
    scan_paused = pyqtSignal()
    scan_resumed = pyqtSignal()
    scan_finished = pyqtSignal(list)

    def __init__(self, config: ConfigManager):
        super().__init__()
        self.config = config
        self.scanner = None
        self.scan_thread = None
        self.scan_worker = None
        self.scan_is_running = False
        
        self.kali_tools = KaliToolIntegration(config)
        self.setWindowTitle("ReplayHunter Pro v2.2 - Kali Linux Edition")
        self.setGeometry(100, 100, 1400, 900)
        self.setStyleSheet(DarkTheme.STYLESHEET)
        self._init_ui()
        self._connect_signals()
        
        if METRICS_AVAILABLE and config.get("metrics.enabled", False):
            try:
                port = config.get("metrics.port", 9090)
                start_http_server(port)
                self.statusBar().showMessage(f"指标服务器运行在 :{port}")
            except Exception as e:
                print(f"[!] 启动 Prometheus 指标服务器失败: {e}")
                self.statusBar().showMessage("指标服务器启动失败")

    def _init_ui(self):
        """初始化 UI 组件"""
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QHBoxLayout(central_widget)
        
        # 创建标签页
        self.tab_widget = QTabWidget()
        
        # 扫描标签页
        scan_tab = QWidget()
        scan_layout = QHBoxLayout(scan_tab)
        
        left_panel = self._create_request_panel()
        center_panel = self._create_control_panel()
        right_panel = self._create_result_panel()
        
        splitter = QSplitter(Qt.Horizontal)
        splitter.addWidget(left_panel)
        splitter.addWidget(center_panel)
        splitter.addWidget(right_panel)
        splitter.setSizes([300, 400, 700])
        
        scan_layout.addWidget(splitter)
        self.tab_widget.addTab(scan_tab, "🔍 扫描")
        
        # 测试过程标签页
        self.test_process_tab = QTextBrowser()
        self.test_process_tab.setOpenExternalLinks(True)
        self.tab_widget.addTab(self.test_process_tab, "📋 测试过程")
        
        # 漏洞详情标签页
        self.vuln_details_tab = QTextBrowser()
        self.vuln_details_tab.setOpenExternalLinks(True)
        self.tab_widget.addTab(self.vuln_details_tab, "🔬 漏洞详情")
        
        main_layout.addWidget(self.tab_widget)

    def _create_request_panel(self) -> QGroupBox:
        """创建请求配置面板"""
        group = QGroupBox("请求配置")
        layout = QVBoxLayout()
        
        layout.addWidget(QLabel("目标 URL:"))
        self.url_input = QLineEdit()
        self.url_input.setPlaceholderText("http://example.com/api/v1/user")
        layout.addWidget(self.url_input)
        
        layout.addWidget(QLabel("HTTP 方法:"))
        self.method_combo = QComboBox()
        self.method_combo.addItems(["POST", "GET", "PUT", "PATCH", "DELETE"])
        layout.addWidget(self.method_combo)
        
        layout.addWidget(QLabel("请求头 (JSON):"))
        self.headers_edit = QPlainTextEdit()
        self.headers_edit.setPlainText(json.dumps({
            "Content-Type": "application/json",
            "Authorization": "Bearer YOUR_TOKEN_HERE"
        }, indent=2))
        self.headers_edit.setMaximumHeight(150)
        layout.addWidget(self.headers_edit)
        
        layout.addWidget(QLabel("请求体:"))
        self.body_edit = QPlainTextEdit()
        self.body_edit.setPlainText('{"user_id": "123", "action": "view"}')
        self.body_edit.setMaximumHeight(150)
        layout.addWidget(self.body_edit)
        
        layout.addWidget(QLabel("Kali 工具集成:"))
        self.kali_group = QGroupBox()
        kali_layout = QVBoxLayout()
        self.sqlmap_check = QCheckBox("sqlmap 深度检测")
        self.sqlmap_check.setChecked(self.kali_tools.is_tool_available("sqlmap"))
        kali_layout.addWidget(self.sqlmap_check)
        self.burp_check = QCheckBox("Burp Suite 代理")
        self.burp_check.setChecked(self.kali_tools.is_tool_available("burp"))
        kali_layout.addWidget(self.burp_check)
        self.kali_group.setLayout(kali_layout)
        layout.addWidget(self.kali_group)
        
        import_btn = QPushButton("📥 导入 Burp 请求")
        import_btn.clicked.connect(self.import_burp_request)
        layout.addWidget(import_btn)
        
        layout.addStretch()
        group.setLayout(layout)
        return group

    def _create_control_panel(self) -> QGroupBox:
        """创建扫描控制面板"""
        group = QGroupBox("扫描控制")
        layout = QVBoxLayout()
        
        btn_layout = QHBoxLayout()
        self.start_btn = QPushButton("🚀 开始扫描")
        self.pause_btn = QPushButton("⏸️ 暂停")
        self.stop_btn = QPushButton("⏹️ 停止")
        self.pause_btn.setEnabled(False)
        self.stop_btn.setEnabled(False)
        btn_layout.addWidget(self.start_btn)
        btn_layout.addWidget(self.pause_btn)
        btn_layout.addWidget(self.stop_btn)
        layout.addLayout(btn_layout)
        
        self.progress_bar = QProgressBar()
        layout.addWidget(self.progress_bar)
        
        layout.addWidget(QLabel("实时日志:"))
        self.log_edit = QTextEdit()
        self.log_edit.setReadOnly(True)
        self.log_edit.setMaximumHeight(300)
        layout.addWidget(self.log_edit)
        
        stats_layout = QHBoxLayout()
        self.req_count_label = QLabel("请求数: 0")
        self.vuln_count_label = QLabel("漏洞: 0")
        self.rate_label = QLabel("速率: 0 req/s")
        stats_layout.addWidget(self.req_count_label)
        stats_layout.addWidget(self.vuln_count_label)
        stats_layout.addWidget(self.rate_label)
        layout.addLayout(stats_layout)
        
        group.setLayout(layout)
        return group

    def _create_result_panel(self) -> QGroupBox:
        """创建结果展示面板"""
        group = QGroupBox("扫描结果")
        layout = QVBoxLayout()
        
        self.vuln_table = QTableWidget()
        self.vuln_table.setColumnCount(6)
        self.vuln_table.setHorizontalHeaderLabels(["类型", "参数", "严重程度", "风险评分", "置信度", "详情"])
        self.vuln_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.vuln_table.itemDoubleClicked.connect(self.show_vulnerability_details)
        layout.addWidget(self.vuln_table)
        
        layout.addWidget(QLabel("响应差异对比:"))
        diff_splitter = QSplitter(Qt.Vertical)
        self.baseline_edit = QPlainTextEdit()
        self.baseline_edit.setReadOnly(True)
        self.baseline_edit.setPlaceholderText("基准响应将显示在这里...")
        diff_splitter.addWidget(self.baseline_edit)
        self.replay_edit = QPlainTextEdit()
        self.replay_edit.setReadOnly(True)
        self.replay_edit.setPlaceholderText("重放响应将显示在这里...")
        diff_splitter.addWidget(self.replay_edit)
        
        self.baseline_highlighter = DiffHighlighter(self.baseline_edit.document())
        self.replay_highlighter = DiffHighlighter(self.replay_edit.document())
        layout.addWidget(diff_splitter)
        
        group.setLayout(layout)
        return group

    def _connect_signals(self):
        """连接信号与槽"""
        self.start_btn.clicked.connect(self.start_scan)
        self.pause_btn.clicked.connect(self.toggle_pause)
        self.stop_btn.clicked.connect(self.stop_scan)
        self.scan_started.connect(self.on_scan_started)
        self.scan_finished.connect(self.on_scan_finished)

    def start_scan(self):
        """启动扫描"""
        if self.scan_is_running:
            QMessageBox.warning(self, "警告", "扫描已在进行中，请先停止或等待完成。")
            return

        url = self.url_input.text().strip()
        if not url:
            QMessageBox.warning(self, "错误", "请输入目标 URL")
            return
        
        try:
            headers = json.loads(self.headers_edit.toPlainText())
        except json.JSONDecodeError:
            QMessageBox.warning(self, "配置错误", "请求头必须是合法的 JSON")
            return
        
        body = self.body_edit.toPlainText()
        data = {}
        if body:
            try:
                data = json.loads(body)
            except json.JSONDecodeError:
                QMessageBox.warning(self, "配置错误", "请求体必须是合法的 JSON")
                return

        # 清空之前的结果
        self.vuln_table.setRowCount(0)
        self.test_process_tab.clear()
        self.vuln_details_tab.clear()
        self.log_edit.clear()
        
        self.scanner = AsyncReplayScanner(self.config)
        target = {
            "url": url,
            "method": self.method_combo.currentText(),
            "headers": headers,
            "data": data
        }
        
        self.scan_thread = QThread()
        self.scan_worker = ScanWorker(self.scanner, target)
        self.scan_worker.moveToThread(self.scan_thread)
        self.scan_worker.log_signal.connect(self.append_log)
        self.scan_worker.progress_signal.connect(self.progress_bar.setValue)
        self.scan_worker.result_signal.connect(self.on_vulnerability_found)
        self.scan_worker.finished_signal.connect(self.on_scan_thread_finished)
        self.scan_thread.started.connect(self.scan_worker.run)
        self.scan_is_running = True
        self.scan_thread.start()
        self.scan_started.emit()

    def toggle_pause(self):
        """暂停/恢复扫描"""
        if self.scan_worker and self.scan_is_running:
            if self.pause_btn.text() == "⏸️ 暂停":
                self.scan_worker.pause()
                self.pause_btn.setText("▶️ 恢复")
                self.statusBar().showMessage("扫描已暂停")
            else:
                self.scan_worker.resume()
                self.pause_btn.setText("⏸️ 暂停")
                self.statusBar().showMessage("扫描进行中...")

    def stop_scan(self):
        """停止扫描"""
        if self.scan_thread and self.scan_thread.isRunning():
            self.scan_worker.stop()
            self.scan_thread.quit()
            self.scan_thread.wait()
            self.scan_is_running = False
            self.scan_thread = None
            self.scan_worker = None
            self.pause_btn.setText("⏸️ 暂停")
            self.scan_finished.emit([])

    def import_burp_request(self):
        """导入 Burp 请求"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择 Burp Suite XML 文件", "", "XML Files (*.xml)"
        )
        if file_path:
            requests = self.kali_tools.import_from_burp(file_path)
            if requests and 'error' not in requests[0]:
                req = requests[0]
                self.url_input.setText(req['url'])
                self.method_combo.setCurrentText(req['method'])
                self.headers_edit.setPlainText(json.dumps(req['headers'], indent=2))
                QMessageBox.information(self, "成功", "请求导入成功！")
            else:
                error_msg = requests[0].get('error', '未知错误') if requests else '未知错误'
                QMessageBox.warning(self, "错误", error_msg)

    def append_log(self, message: str):
        """追加日志"""
        self.log_edit.append(message)
        self.req_count_label.setText(f"请求数: {self.scanner.test_process.requests_sent if self.scanner else 0}")

    def on_vulnerability_found(self, vuln: Vulnerability):
        """发现漏洞时的处理"""
        for row in range(self.vuln_table.rowCount()):
            if self.vuln_table.item(row, 0).text() == vuln.type and \
               self.vuln_table.item(row, 1).text() == vuln.param:
                existing_confidence = float(self.vuln_table.item(row, 4).text().rstrip('%')) / 100
                if vuln.confidence > existing_confidence:
                    self.vuln_table.setItem(row, 3, QTableWidgetItem(str(vuln.risk_score)))
                    self.vuln_table.setItem(row, 4, QTableWidgetItem(f"{vuln.confidence:.0%}"))
                    self.vuln_table.setItem(row, 5, QTableWidgetItem(vuln.evidence))
                return
        
        row = self.vuln_table.rowCount()
        self.vuln_table.insertRow(row)
        self.vuln_table.setItem(row, 0, QTableWidgetItem(vuln.type))
        self.vuln_table.setItem(row, 1, QTableWidgetItem(vuln.param))
        
        # 设置严重程度颜色
        severity_item = QTableWidgetItem(vuln.severity)
        color = Vulnerability.RISK_LEVELS.get(vuln.severity, {}).get("color", "#6c757d")
        severity_item.setForeground(QColor(color))
        self.vuln_table.setItem(row, 2, severity_item)
        
        self.vuln_table.setItem(row, 3, QTableWidgetItem(str(vuln.risk_score)))
        self.vuln_table.setItem(row, 4, QTableWidgetItem(f"{vuln.confidence:.0%}"))
        self.vuln_table.setItem(row, 5, QTableWidgetItem(vuln.evidence))
        
        # 存储完整漏洞对象
        self.vuln_table.item(row, 0).setData(Qt.UserRole, vuln)
        
        self.vuln_count_label.setText(f"漏洞: {row + 1}")

    def show_vulnerability_details(self, item):
        """显示漏洞详情"""
        row = item.row()
        vuln = self.vuln_table.item(row, 0).data(Qt.UserRole)
        if vuln:
            html = self._generate_vulnerability_html(vuln)
            self.vuln_details_tab.setHtml(html)
            self.tab_widget.setCurrentWidget(self.vuln_details_tab)

    def _generate_vulnerability_html(self, vuln: Vulnerability) -> str:
        """生成漏洞详情HTML"""
        risk_level = Vulnerability.RISK_LEVELS.get(vuln.severity, {})
        color = risk_level.get("color", "#6c757d")
        
        html = f"""
        <!DOCTYPE html>
        <html>
        <head>
            <style>
                body {{
                    font-family: 'Segoe UI', sans-serif;
                    background: #1e1e1e;
                    color: #d4d4d4;
                    padding: 20px;
                }}
                .header {{
                    border-left: 5px solid {color};
                    padding-left: 15px;
                    margin-bottom: 20px;
                }}
                .header h1 {{
                    color: {color};
                    margin: 0;
                }}
                .section {{
                    background: #252526;
                    padding: 15px;
                    margin: 15px 0;
                    border-radius: 4px;
                    border: 1px solid #3e3e42;
                }}
                .section h3 {{
                    color: #007acc;
                    margin-top: 0;
                }}
                .metric {{
                    display: inline-block;
                    margin: 10px 20px 10px 0;
                }}
                .metric-label {{
                    color: #888;
                    font-size: 12px;
                }}
                .metric-value {{
                    font-size: 24px;
                    font-weight: bold;
                    color: {color};
                }}
                .code {{
                    background: #1e1e1e;
                    padding: 10px;
                    border-radius: 4px;
                    font-family: 'Courier New', monospace;
                    overflow-x: auto;
                    border: 1px solid #3e3e42;
                }}
                ul {{
                    padding-left: 20px;
                }}
                li {{
                    margin: 8px 0;
                }}
                .cvss {{
                    background: #2d2d30;
                    padding: 8px;
                    border-radius: 4px;
                    font-family: 'Courier New', monospace;
                    color: #4ade80;
                }}
            </style>
        </head>
        <body>
            <div class="header">
                <h1>[{vuln.severity}] {vuln.type}</h1>
                <p>{risk_level.get('description', '')}</p>
            </div>
            
            <div class="section">
                <h3>📊 风险评估</h3>
                <div class="metric">
                    <div class="metric-label">风险评分</div>
                    <div class="metric-value">{vuln.risk_score}/10</div>
                </div>
                <div class="metric">
                    <div class="metric-label">置信度</div>
                    <div class="metric-value">{vuln.confidence:.0%}</div>
                </div>
                <div class="metric">
                    <div class="metric-label">严重程度</div>
                    <div class="metric-value">{vuln.severity}</div>
                </div>
            </div>
            
            <div class="section">
                <h3>🔍 漏洞详情</h3>
                <p><strong>参数:</strong> {vuln.param}</p>
                <p><strong>Payload:</strong> <code>{vuln.payload}</code></p>
                <p><strong>证据:</strong> {vuln.evidence}</p>
                <p><strong>发现时间:</strong> {vuln.timestamp.strftime('%Y-%m-%d %H:%M:%S')}</p>
            </div>
            
            <div class="section">
                <h3>⚠️ 漏洞危害</h3>
                <p><strong>影响描述:</strong></p>
                <p>{vuln.impact.get('impact', '未知影响')}</p>
                <p><strong>潜在后果:</strong></p>
                <ul>
                    {''.join(f'<li>{c}</li>' for c in vuln.impact.get('consequences', []))}
                </ul>
                <p><strong>CVSS 向量:</strong></p>
                <div class="cvss">{vuln.impact.get('cvss_vector', 'N/A')}</div>
            </div>
            
            <div class="section">
                <h3>🛠️ 修复建议</h3>
                <p><strong>立即修复 (紧急):</strong></p>
                <ul>
                    {''.join(f'<li>{r}</li>' for r in vuln.remediation.get('immediate', []))}
                </ul>
                <p><strong>短期修复 (1-2周):</strong></p>
                <ul>
                    {''.join(f'<li>{r}</li>' for r in vuln.remediation.get('short_term', []))}
                </ul>
                <p><strong>长期改进 (1-3月):</strong></p>
                <ul>
                    {''.join(f'<li>{r}</li>' for r in vuln.remediation.get('long_term', []))}
                </ul>
            </div>
            
            <div class="section">
                <h3>💻 代码示例</h3>
                <div class="code">
                    <pre>{vuln.remediation.get('code_example', '请参考安全编码指南')}</pre>
                </div>
            </div>
            
            <div class="section">
                <h3>🔬 测试详情</h3>
                <pre>{json.dumps(vuln.test_details, indent=2)}</pre>
            </div>
        </body>
        </html>
        """
        return html

    def on_scan_started(self):
        """扫描开始"""
        self.start_btn.setEnabled(False)
        self.pause_btn.setEnabled(True)
        self.pause_btn.setText("⏸️ 暂停")
        self.stop_btn.setEnabled(True)
        self.statusBar().showMessage("扫描进行中...")

    def on_scan_finished(self, vulns: List[Vulnerability]):
        """扫描完成"""
        self.start_btn.setEnabled(True)
        self.pause_btn.setEnabled(False)
        self.pause_btn.setText("⏸️ 暂停")
        self.stop_btn.setEnabled(False)
        self.statusBar().showMessage(f"扫描完成，发现 {len(vulns)} 个漏洞")
        
        # 显示测试过程
        if self.scanner and self.scanner.test_process:
            test_process_html = self._generate_test_process_html()
            self.test_process_tab.setHtml(test_process_html)
        
        # 保存报告
        report_file = self.save_report(vulns)
        QMessageBox.information(self, "完成", f"扫描完成！报告已保存至:\n{report_file}")

    def _generate_test_process_html(self) -> str:
        """生成测试过程HTML"""
        test_process = self.scanner.test_process
        
        html = """
        <!DOCTYPE html>
        <html>
        <head>
            <style>
                body {
                    font-family: 'Segoe UI', sans-serif;
                    background: #1e1e1e;
                    color: #d4d4d4;
                    padding: 20px;
                }
                .timeline {
                    position: relative;
                    padding: 20px 0;
                }
                .timeline-item {
                    position: relative;
                    padding: 15px;
                    margin: 10px 0;
                    background: #252526;
                    border-radius: 4px;
                    border-left: 3px solid #007acc;
                }
                .timeline-time {
                    color: #888;
                    font-size: 12px;
                }
                .timeline-title {
                    font-weight: bold;
                    color: #007acc;
                    margin: 5px 0;
                }
                .timeline-description {
                    color: #d4d4d4;
                }
                .timeline-details {
                    margin-top: 10px;
                    padding: 10px;
                    background: #1e1e1e;
                    border-radius: 4px;
                    font-size: 12px;
                }
                .level-INFO { border-left-color: #17a2b8; }
                .level-SUCCESS { border-left-color: #28a745; }
                .level-WARNING { border-left-color: #ffc107; }
                .level-ERROR { border-left-color: #dc3545; }
                .summary {
                    background: #252526;
                    padding: 20px;
                    border-radius: 4px;
                    margin-bottom: 20px;
                }
                .summary h2 {
                    color: #007acc;
                    margin-top: 0;
                }
            </style>
        </head>
        <body>
            <div class="summary">
                <h2>📋 测试摘要</h2>
                <p><strong>开始时间:</strong> {start_time}</p>
                <p><strong>结束时间:</strong> {end_time}</p>
                <p><strong>总耗时:</strong> {duration:.2f} 秒</p>
                <p><strong>发送请求:</strong> {requests} 个</p>
                <p><strong>接收响应:</strong> {responses} 个</p>
                <p><strong>错误数:</strong> {errors} 个</p>
            </div>
            <div class="timeline">
        """.format(
            start_time=test_process.start_time.strftime('%Y-%m-%d %H:%M:%S') if test_process.start_time else 'N/A',
            end_time=test_process.end_time.strftime('%Y-%m-%d %H:%M:%S') if test_process.end_time else 'N/A',
            duration=(test_process.end_time - test_process.start_time).total_seconds() if test_process.end_time and test_process.start_time else 0,
            requests=test_process.requests_sent,
            responses=test_process.responses_received,
            errors=test_process.errors_encountered
        )
        
        for step in test_process.steps:
            html += f"""
            <div class="timeline-item level-{step['level']}">
                <div class="timeline-time">{step['timestamp']}</div>
                <div class="timeline-title">{step['title']}</div>
                <div class="timeline-description">{step['description']}</div>
            """
            
            if step['details']:
                html += "<div class='timeline-details'>"
                for key, value in step['details'].items():
                    html += f"<div><strong>{key}:</strong> {value}</div>"
                html += "</div>"
            
            html += "</div>"
        
        html += """
            </div>
        </body>
        </html>
        """
        return html

    def on_scan_thread_finished(self, vulns: List[Vulnerability]):
        """扫描线程结束"""
        self.scan_is_running = False
        if self.scan_thread:
            self.scan_thread.quit()
            self.scan_thread.wait()
            self.scan_thread = None
        if self.scan_worker:
            self.scan_worker = None
        self.scan_finished.emit(vulns)

    def save_report(self, vulns: List[Vulnerability]) -> str:
        """保存增强版 HTML 报告"""
        filename = f"replay_report_{int(time.time())}.html"
        
        # 生成报告HTML
        html = f"""
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <title>ReplayHunter Pro v2.2 扫描报告</title>
            <style>
                body {{
                    font-family: 'Segoe UI', sans-serif;
                    background: #f5f5f5;
                    margin: 0;
                    padding: 20px;
                }}
                .container {{
                    max-width: 1400px;
                    margin: 0 auto;
                    background: white;
                    padding: 40px;
                    border-radius: 8px;
                    box-shadow: 0 2px 10px rgba(0,0,0,0.1);
                }}
                h1 {{
                    color: #007acc;
                    border-bottom: 3px solid #007acc;
                    padding-bottom: 15px;
                }}
                .summary {{
                    display: flex;
                    justify-content: space-around;
                    margin: 30px 0;
                }}
                .metric {{
                    text-align: center;
                    padding: 20px;
                    background: #f8f9fa;
                    border-radius: 8px;
                    flex: 1;
                    margin: 0 10px;
                }}
                .metric-value {{
                    font-size: 36px;
                    font-weight: bold;
                    color: #007acc;
                }}
                .metric-label {{
                    color: #666;
                    margin-top: 10px;
                }}
                .vuln-card {{
                    background: #fff;
                    padding: 25px;
                    margin: 20px 0;
                    border-left: 5px solid #f44336;
                    border-radius: 4px;
                    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
                }}
                .vuln-high {{ border-left-color: #f44336; }}
                .vuln-medium {{ border-left-color: #ff9800; }}
                .vuln-low {{ border-left-color: #4caf50; }}
                .vuln-info {{ border-left-color: #6c757d; }}
                .vuln-header {{
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                    margin-bottom: 15px;
                }}
                .vuln-title {{
                    font-size: 20px;
                    font-weight: bold;
                    color: #333;
                }}
                .risk-badge {{
                    padding: 5px 15px;
                    border-radius: 20px;
                    color: white;
                    font-weight: bold;
                }}
                .risk-critical {{ background: #dc3545; }}
                .risk-high {{ background: #fd7e14; }}
                .risk-medium {{ background: #ffc107; color: #333; }}
                .risk-low {{ background: #28a745; }}
                .risk-info {{ background: #6c757d; }}
                .vuln-details {{
                    margin: 15px 0;
                }}
                .detail-item {{
                    margin: 10px 0;
                    padding: 10px;
                    background: #f8f9fa;
                    border-radius: 4px;
                }}
                .detail-label {{
                    font-weight: bold;
                    color: #666;
                    margin-right: 10px;
                }}
                .code {{
                    background: #1e1e1e;
                    color: #d4d4d4;
                    padding: 15px;
                    border-radius: 4px;
                    font-family: 'Courier New', monospace;
                    overflow-x: auto;
                    margin: 10px 0;
                }}
                .remediation {{
                    background: #e7f3ff;
                    padding: 15px;
                    border-radius: 4px;
                    margin: 15px 0;
                }}
                .remediation h4 {{
                    color: #007acc;
                    margin-top: 0;
                }}
                ul {{
                    padding-left: 20px;
                }}
                li {{
                    margin: 8px 0;
                }}
                .test-process {{
                    background: #f8f9fa;
                    padding: 20px;
                    border-radius: 4px;
                    margin: 20px 0;
                }}
                .timeline {{
                    position: relative;
                    padding: 20px 0;
                }}
                .timeline-item {{
                    position: relative;
                    padding: 15px;
                    margin: 10px 0;
                    background: white;
                    border-radius: 4px;
                    border-left: 3px solid #007acc;
                }}
                .level-INFO {{ border-left-color: #17a2b8; }}
                .level-SUCCESS {{ border-left-color: #28a745; }}
                .level-WARNING {{ border-left-color: #ffc107; }}
                .level-ERROR {{ border-left-color: #dc3545; }}
            </style>
        </head>
        <body>
            <div class="container">
                <h1>🔍 ReplayHunter Pro v2.2 扫描报告</h1>
                <p><strong>生成时间:</strong> {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
                
                <div class="summary">
                    <div class="metric">
                        <div class="metric-value">{len(vulns)}</div>
                        <div class="metric-label">发现漏洞</div>
                    </div>
                    <div class="metric">
                        <div class="metric-value">{sum(1 for v in vulns if v.severity in ['CRITICAL', 'HIGH'])}</div>
                        <div class="metric-label">高危漏洞</div>
                    </div>
                    <div class="metric">
                        <div class="metric-value">{max([v.risk_score for v in vulns], default=0):.1f}</div>
                        <div class="metric-label">最高风险评分</div>
                    </div>
                </div>
        """
        
        # 添加测试过程
        if self.scanner and self.scanner.test_process:
            html += self.scanner.test_process.to_html()
        
        # 添加漏洞详情
        html += "<h2>🔬 漏洞详情</h2>"
        for vuln in vulns:
            severity_class = vuln.severity.lower()
            html += f"""
                <div class="vuln-card vuln-{severity_class}">
                    <div class="vuln-header">
                        <div class="vuln-title">{vuln.type}</div>
                        <div class="risk-badge risk-{severity_class}">{vuln.severity}</div>
                    </div>
                    
                    <div class="vuln-details">
                        <div class="detail-item">
                            <span class="detail-label">风险评分:</span>
                            <span style="color: {Vulnerability.RISK_LEVELS.get(vuln.severity, {}).get('color', '#000')}; font-weight: bold; font-size: 18px;">
                                {vuln.risk_score}/10
                            </span>
                        </div>
                        <div class="detail-item">
                            <span class="detail-label">置信度:</span>
                            <span>{vuln.confidence:.0%}</span>
                        </div>
                        <div class="detail-item">
                            <span class="detail-label">参数:</span>
                            <code>{vuln.param}</code>
                        </div>
                        <div class="detail-item">
                            <span class="detail-label">Payload:</span>
                            <code>{vuln.payload}</code>
                        </div>
                        <div class="detail-item">
                            <span class="detail-label">证据:</span>
                            <span>{vuln.evidence}</span>
                        </div>
                    </div>
                    
                    <div class="remediation">
                        <h4>⚠️ 漏洞危害</h4>
                        <p>{vuln.impact.get('impact', '未知影响')}</p>
                        <p><strong>潜在后果:</strong></p>
                        <ul>
                            {''.join(f'<li>{c}</li>' for c in vuln.impact.get('consequences', []))}
                        </ul>
                        <p><strong>CVSS 向量:</strong> <code>{vuln.impact.get('cvss_vector', 'N/A')}</code></p>
                    </div>
                    
                    <div class="remediation">
                        <h4>🛠️ 修复建议</h4>
                        <p><strong>立即修复:</strong></p>
                        <ul>
                            {''.join(f'<li>{r}</li>' for r in vuln.remediation.get('immediate', []))}
                        </ul>
                        <p><strong>短期修复:</strong></p>
                        <ul>
                            {''.join(f'<li>{r}</li>' for r in vuln.remediation.get('short_term', []))}
                        </ul>
                        <p><strong>长期改进:</strong></p>
                        <ul>
                            {''.join(f'<li>{r}</li>' for r in vuln.remediation.get('long_term', []))}
                        </ul>
                        <p><strong>代码示例:</strong></p>
                        <div class="code">
                            <pre>{vuln.remediation.get('code_example', '请参考安全编码指南')}</pre>
                        </div>
                    </div>
                </div>
            """
        
        html += """
            </div>
        </body>
        </html>
        """
        
        try:
            with open(filename, 'w', encoding='utf-8') as f:
                f.write(html)
        except Exception as e:
            print(f"[!] 保存报告失败: {e}")
            filename = f"保存失败_{filename}"
        return filename

class ScanWorker(QObject):
    """后台扫描工作线程"""
    log_signal = pyqtSignal(str)
    progress_signal = pyqtSignal(int)
    result_signal = pyqtSignal(Vulnerability)
    finished_signal = pyqtSignal(list)
    stop_signal = pyqtSignal()
    pause_signal = pyqtSignal()
    resume_signal = pyqtSignal()

    def __init__(self, scanner: AsyncReplayScanner, target: Dict[str, Any]):
        super().__init__()
        self.scanner = scanner
        self.target = target
        self._stop_flag = False
        self._pause_flag = False
        self.stop_signal.connect(self._set_stop_flag)
        self.pause_signal.connect(self._set_pause_flag)
        self.resume_signal.connect(self._clear_pause_flag)

    def _set_stop_flag(self):
        self._stop_flag = True

    def _set_pause_flag(self):
        self._pause_flag = True

    def _clear_pause_flag(self):
        self._pause_flag = False

    def stop(self):
        self.stop_signal.emit()

    def pause(self):
        self.pause_signal.emit()

    def resume(self):
        self.resume_signal.emit()

    def run(self):
        """执行扫描"""
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        
        def progress_callback(msg: str):
            if not self._stop_flag:
                self.log_signal.emit(msg)
        
        try:
            if not self._stop_flag:
                while self._pause_flag and not self._stop_flag:
                    time.sleep(0.1)
                
                if not self._stop_flag:
                    vulns = loop.run_until_complete(
                        self.scanner.scan(self.target, progress_callback)
                    )
                    if not self._stop_flag:
                        self.finished_signal.emit(vulns)
                    else:
                        self.finished_signal.emit([])
                else:
                    self.finished_signal.emit([])
            else:
                self.finished_signal.emit([])
        except Exception as e:
            self.log_signal.emit(f"扫描异常: {str(e)}")
            self.finished_signal.emit([])
        finally:
            loop.close()

# ==================== 8. CLI 界面（保持原有功能）====================
class CLIApp:
    """Rich 增强的命令行界面"""
    def __init__(self, config: ConfigManager):
        self.config = config
        self.console = Console() if CLI_AVAILABLE else None
        self.scanner = AsyncReplayScanner(config)
        self.kali_tools = KaliToolIntegration(config)

    def print_banner(self):
        """打印 Kali 风格 Banner"""
        if self.console:
            banner = r"""
[bold blue]    ____  ____________    __  __            __           
   / __ \/ ____/ ____/   / / / /_  ______  / /____  _____
  / /_/ / /   / __/     / /_/ / / / / __ \/ __/ _ \/ ___/
 / _, _/ /___/ /___    / __  / /_/ / / / / /_/  __/ /    
/_/ |_|\____/_____/   /_/ /_/\__,_/_/ /_/\__/\___/_/     
[/bold blue]
[bold yellow]:: Kali Linux Edition :: v2.2 Pro :: Session Replay Scanner ::[/bold yellow]
            """
            self.console.print(Panel(banner, style="bold white"))
        else:
            print(r"""
    ____  ____________    __  __            __           
   / __ \/ ____/ ____/   / / / /_  ______  / /____  _____
  / /_/ / /   / __/     / /_/ / / / / __ \/ __/ _ \/ ___/
 / _, _/ /___/ /___    / __  / /_/ / / / / /_/  __/ /    
/_/ |_|\____/_____/   /_/ /_/\__,_/_/ /_/\__/\___/_/     

:: Kali Linux Edition :: v2.2 Pro :: Session Replay Scanner ::
            """)

    def run_interactive(self):
        """交互式模式"""
        self.print_banner()
        
        if self.config.get("security.require_auth", True):
            if self.console:
                self.console.print("\n[bold red]⚠️  法律警告[/bold red]")
                self.console.print("本工具仅用于授权的安全测试")
                confirm = input("\n输入 'YES' 确认授权: ")
            else:
                print("\n⚠️  法律警告")
                print("本工具仅用于授权的安全测试")
                confirm = input("\n输入 'YES' 确认授权: ")
            
            if confirm.strip().upper() != "YES":
                if self.console:
                    self.console.print("[red]未授权，退出。[/red]")
                else:
                    print("未授权，退出。")
                return

        url = input("目标 URL: ").strip()
        method = input("HTTP 方法 [POST]: ").strip().upper() or "POST"
        
        headers = {}
        if input("是否添加请求头? (y/n) ").lower() == 'y':
            print("输入 JSON 格式请求头，按 Ctrl+D (或 Ctrl+Z) 结束:")
            header_lines = []
            try:
                while True:
                    line = input()
                    header_lines.append(line)
            except EOFError:
                pass
            try:
                headers = json.loads('\n'.join(header_lines))
            except json.JSONDecodeError:
                print("无效的 JSON")
                return

        data = {}
        if method in ["POST", "PUT", "PATCH"]:
            print("输入请求体 JSON，按 Ctrl+D (或 Ctrl+Z) 结束:")
            body_lines = []
            try:
                while True:
                    line = input()
                    body_lines.append(line)
            except EOFError:
                pass
            try:
                data = json.loads('\n'.join(body_lines))
            except json.JSONDecodeError:
                print("无效的 JSON")
                return

        target = {
            "url": url,
            "method": method,
            "headers": headers,
            "data": data
        }

        if CLI_AVAILABLE and self.console:
            with Progress(
                SpinnerColumn(),
                TextColumn("[progress.description]{task.description}"),
                BarColumn(),
                TaskProgressColumn(),
                console=self.console
            ) as progress:
                task = progress.add_task("[cyan]正在扫描...", total=100)
                def update_progress(msg: str):
                    progress.update(task, advance=20, description=msg)
                vulns = asyncio.run(self.scanner.scan(target, update_progress))
                progress.update(task, completed=100)
        else:
            print("正在扫描...")
            def simple_progress(msg: str):
                print(f"  {msg}")
            vulns = asyncio.run(self.scanner.scan(target, simple_progress))
            print("扫描完成!")

        self.display_results(vulns)

        save_choice = input("\n保存报告? (y/n) ").lower()
        if vulns and save_choice == 'y':
            filename = f"replay_report_{int(time.time())}.html"
            self.save_cli_report(vulns, filename)
            print(f"报告已保存: {filename}")

    def display_results(self, vulns: List[Vulnerability]):
        """显示扫描结果表格"""
        if not vulns:
            if self.console:
                self.console.print("[green]✅ 未发现漏洞[/green]")
            else:
                print("✅ 未发现漏洞")
            return

        if CLI_AVAILABLE and self.console:
            table = Table(title="漏洞详情", show_header=True, header_style="bold magenta")
            table.add_column("类型", style="cyan")
            table.add_column("参数", style="yellow")
            table.add_column("严重程度", style="red")
            table.add_column("风险评分", justify="right")
            table.add_column("置信度", justify="right")
            table.add_column("证据", style="dim")
            
            for vuln in vulns:
                severity_color = {
                    "CRITICAL": "bold red",
                    "HIGH": "red",
                    "MEDIUM": "yellow",
                    "LOW": "green",
                    "INFO": "dim"
                }.get(vuln.severity, "white")
                
                table.add_row(
                    vuln.type,
                    vuln.param,
                    f"[{severity_color}]{vuln.severity}[/{severity_color}]",
                    f"{vuln.risk_score:.1f}/10",
                    f"{vuln.confidence:.0%}",
                    vuln.evidence[:60] + "..." if len(vuln.evidence) > 60 else vuln.evidence
                )
            
            self.console.print(table)
            self.console.print(f"\n[bold red]⚠️  发现 {len(vulns)} 个漏洞![/bold red]")
        else:
            print(f"\n⚠️  发现 {len(vulns)} 个漏洞!")
            for i, vuln in enumerate(vulns):
                print(f"  [{i+1}] [{vuln.severity}] {vuln.type}")
                print(f"      参数: {vuln.param}")
                print(f"      风险评分: {vuln.risk_score}/10")
                print(f"      置信度: {vuln.confidence:.0%}")
                print(f"      证据: {vuln.evidence}")

    def save_cli_report(self, vulns: List[Vulnerability], filename: str):
        """保存 CLI 报告"""
        # 使用与GUI相同的报告生成逻辑
        html = f"""
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <title>ReplayHunter Pro v2.2 CLI 报告</title>
            <style>
                body {{
                    font-family: 'Segoe UI', sans-serif;
                    margin: 40px;
                    background: #f5f5f5;
                }}
                .container {{
                    max-width: 1200px;
                    margin: 0 auto;
                    background: white;
                    padding: 30px;
                    border-radius: 8px;
                    box-shadow: 0 2px 10px rgba(0,0,0,0.1);
                }}
                h1 {{
                    color: #007acc;
                    border-bottom: 3px solid #007acc;
                    padding-bottom: 15px;
                }}
                .vuln {{
                    border-left: 4px solid #f44336;
                    padding: 15px;
                    margin: 15px 0;
                    background: #fff;
                    border-radius: 4px;
                }}
                .vuln-high {{ border-left-color: #f44336; }}
                .vuln-medium {{ border-left-color: #ff9800; }}
                .vuln-low {{ border-left-color: #4caf50; }}
                .vuln-info {{ border-left-color: #6c757d; }}
                .metric {{
                    display: inline-block;
                    margin: 10px 30px 10px 0;
                }}
                .metric-value {{
                    font-size: 28px;
                    font-weight: bold;
                    color: #007acc;
                }}
                .confidence {{
                    color: #4ade80;
                    font-weight: bold;
                    font-size: 18px;
                }}
                .timestamp {{
                    color: #888;
                    font-size: 14px;
                }}
            </style>
        </head>
        <body>
            <div class="container">
                <h1>🔍 ReplayHunter Pro v2.2 扫描报告</h1>
                <p class="timestamp">生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
                <div>
                    <span class="metric">
                        <div class="metric-value">{len(vulns)}</div>
                        <div>发现漏洞</div>
                    </span>
                    <span class="metric">
                        <div class="metric-value">{sum(1 for v in vulns if v.severity in ['CRITICAL', 'HIGH'])}</div>
                        <div>高危漏洞</div>
                    </span>
                </div>
                <h2>漏洞详情</h2>
        """
        
        for vuln in vulns:
            severity_class = vuln.severity.lower()
            html += f"""
                <div class="vuln vuln-{severity_class}">
                    <h3>[{vuln.severity}] {vuln.type}</h3>
                    <p><strong>参数:</strong> <code>{vuln.param}</code></p>
                    <p><strong>Payload:</strong> <code>{vuln.payload}</code></p>
                    <p><strong>证据:</strong> {vuln.evidence}</p>
                    <p><strong>风险评分:</strong> <span style="color: {Vulnerability.RISK_LEVELS.get(vuln.severity, {}).get('color', '#000')}; font-weight: bold;">{vuln.risk_score}/10</span></p>
                    <p><strong>置信度:</strong> <span class="confidence">{vuln.confidence:.0%}</span></p>
                    <h4>漏洞危害:</h4>
                    <p>{vuln.impact.get('impact', '未知影响')}</p>
                    <h4>修复建议:</h4>
                    <ul>
                        {''.join(f'<li>{r}</li>' for r in vuln.remediation.get('immediate', []))}
                    </ul>
                </div>
            """
        
        html += """
            </div>
        </body>
        </html>
        """
        
        try:
            with open(filename, 'w', encoding='utf-8') as f:
                f.write(html)
        except Exception as e:
            print(f"[!] 保存报告失败: {e}")

# ==================== 9. 主入口 ====================
def main():
    """程序主入口"""
    parser = argparse.ArgumentParser(
        description="ReplayHunter Pro v2.2 - 企业级会话重放检测系统",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
使用示例:
  # GUI 模式 (推荐)
  python3 replayhunter_pro.py --gui
  
  # CLI 交互模式
  python3 replayhunter_pro.py --cli
  
  # 快速扫描
  python3 replayhunter_pro.py --url http://target.com --method POST --data '{"token": "abc"}'
  
  # 加载配置文件
  python3 replayhunter_pro.py --config ~/.replayhunter/prod.yaml --gui
        """
    )
    parser.add_argument("--gui", action="store_true", help="启动图形界面")
    parser.add_argument("--cli", action="store_true", help="启动命令行界面")
    parser.add_argument("--url", help="目标 URL")
    parser.add_argument("--method", default="POST", help="HTTP 方法")
    parser.add_argument("--headers", help="请求头 JSON")
    parser.add_argument("--data", help="请求体 JSON")
    parser.add_argument("--config", help="配置文件路径")
    parser.add_argument("--auth", help="授权令牌")

    args = parser.parse_args()

    config = ConfigManager(args.config or "~/.replayhunter/config.yaml")

    if args.auth:
        config.set("security.auth_token", args.auth)

    if args.gui and GUI_AVAILABLE:
        app = QApplication(sys.argv)
        app.setStyle("Fusion")
        window = MainWindow(config)
        window.show()
        sys.exit(app.exec_())
    elif args.cli or (len(sys.argv) == 1 and not args.gui):
        cli = CLIApp(config)
        cli.run_interactive()
    elif args.url:
        cli = CLIApp(config)
        
        headers = {}
        if args.headers:
            try:
                headers = json.loads(args.headers)
            except json.JSONDecodeError:
                print("[-] 请求头 JSON 格式错误")
                sys.exit(1)
        
        data = {}
        if args.data:
            try:
                data = json.loads(args.data)
            except json.JSONDecodeError:
                print("[-] 请求体 JSON 格式错误")
                sys.exit(1)

        target = {
            "url": args.url,
            "method": args.method,
            "headers": headers,
            "data": data
        }

        vulns = asyncio.run(cli.scanner.scan(target))
        cli.display_results(vulns)
    else:
        parser.print_help()

if __name__ == "__main__":
    if os.path.exists("/etc/os-release"):
        try:
            with open("/etc/os-release") as f:
                if "kali" not in f.read().lower():
                    print("[!] 警告: 未在 Kali Linux 中运行，部分功能可能受限")
        except Exception as e:
            print(f"[!] 检查系统信息时出错: {e}")

    try:
        main()
    except KeyboardInterrupt:
        print("\n用户中断，退出。")
        sys.exit(0)
    except Exception as e:
        print(f"\n致命错误: {e}")
        import traceback
        traceback.print_exc()
        sys.exit(1)
